/*
 * aurora - Communications with Magnetek Aurora Inverter
 *
 * Copyright (C) 2006-2010 Curtis J. Blank curt@curtronics.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program may be used and hosted free of charge by anyone for
 * personal purposes as long as this and all included copyright
 * notice(s) remain intact.
 *
 * Obtain permission before selling the code for this program or
 * hosting this software on a commercial website, excluding if it is 
 * solely hosted for distribution, that is allowable . In all cases
 * copyright must remain intact.
 *
 * This work based on Magnetek's 'Aurora PV Inverter - Communications
 * Protocol -' document, Rel. 1.8 09/05/2005
 * Starting with v1.5-0 this work based on Power One's 'Aurora Inverter
 * Series - Communication Protocol -' document, Rel. 4.6 25/02/09
 *
 * Special thanks to Tron Melzl at Magnetek for all his help including,
 * but not limited to, supplying the Communications Protocol document
 *
 * modified 17-jul-2006 cjb	1. v1.2-6 Last 7 Days production value has been dropped 
 *				   in the v2.3 Communications Protocol doc
 * modified 27-jul-2006 cjb	1. add bVerbose in ClrSerLock
 * modified 13-oct-2006 cjb	1. make this v1.3-0
 * modified 25-apr-2007 cjb	1. take into account Daylight Savings Time when setting the Aurora's time
 * modified 29-dec-2008 cjb	1. correct an error in strftime that only may show up in the first or last
 * 				   week of the year (%G vs %Y)
 * modified 19-aug-2009 cjb	1. fix determining if serial port is in use
 * modified 22-aug-2009 cjb	1. disable XON/XOFF flow control on output
 * modified 29-sep-2009 cjb	1. don't set lckCNT = -1 when clearing stale serial port lock
 * modified 12-oct-2009 cjb	1. add -o option to output data to a file
 * modified 25-oct-2009 cjb	1. serial port configuration tweaks
 * modified 17-oct-2010 cjb	1. added -x option to enable XON/XOFF
 * modified 07-mar-2010 cjb	1. added -Y retries option and -y option to report the number of attempt made
 * modified 13-mar-2010 cjb	1. added -P option to delay read after sending command to inverter
 *				2. rename -P to -U and add -u to report it
 *				3. changed -U seconds to wait to milli-seconds to wait
 * modified 28-mar-2010 cjb	1. working on adding more DSP information
 * modified 27-jul-2010 cjb	1. added -P option to throttle commands sent to the inverter
 * modified 21-sep-2010 cjb	1. added -A option to read "Last four alarms"
 *
 */

static char	VersionM[] = "1.6.7";
char     VersionC[6];

#include <syscall.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include <math.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
#include <errno.h>
#include <error.h>
#include "include/main.h"
#include "include/comm.h"
#include "include/names.h"

BOOL bVerbose = FALSE;
BOOL bColumns = FALSE;			/* Output data in columns */
BOOL bGetDSP = FALSE;			/* Measure request to the DSP */
BOOL bGetDSPEntedned = FALSE;		/* Measure request to the DSP more parameters */
BOOL bGetDSP3Phase = FALSE;		/* Measure request to the DSP three-phase parameter */
BOOL bHideDSP = FALSE;
BOOL bGetEnergy = FALSE;		/* Cumulated Energy Readings */
BOOL bGetInvTime = FALSE;		/* Display Inverter time flag */
BOOL bGetLocTime = FALSE;		/* Display computer time flag */
BOOL bCommCheck = FALSE;
BOOL bColOutput = FALSE;
BOOL bCalcGridPwr = FALSE;
BOOL bXonXoff = FALSE;
BOOL bRptReties = FALSE;
BOOL bRptReadPause = FALSE;
BOOL bGetMDE = FALSE;
int yTimeout = 0;			/* read timeout value in uS */
long PID;
int yMaxAttempts = 1;
int yReadPause = 0;
long int yCommPause = 0;
struct timeval lastcommtv;

FILE *outfp;

char RunTime[18] = " ";
char VersionSHc[6];
int ModelID = 0;

float yCost = 0.0;

/* local Data */
static char VersionCHc[6] = VersionCH;
static char VersionMHc[6] = VersionMH;
static char VersionNHc[6] = VersionNH;
static int yAddress = 0;			/* Inverter address 1-31 */
static char szttyDevice[255];			/* Serial device string */
static char outfile[512];			/* output file FQN */
static BOOL bHelp = FALSE;			/* Print the help text */
static BOOL bGetPN = FALSE;			/* Get Inverter Part Number */
static BOOL bGetVer = FALSE;			/* Get version string */
static BOOL bGetVerFW = FALSE;			/* Get firmware version string */
static BOOL bGetSN = FALSE;			/* Get Serial Number */
static BOOL bGetMfg = FALSE;			/* Get Manufacturing Date */
static BOOL bGetConf = FALSE;			/* Get System Configuration */
static BOOL bGetState = FALSE;			/* State request */
static BOOL bGetJoules = FALSE;			/* Energy cumulated in the last 10 seconds */
static BOOL bGetCount = FALSE;			/* Get Time Counters */
static BOOL bSetTime = FALSE;			/* Set time flag */
static BOOL bVersion = FALSE;			/* Display program version */
static BOOL bGetTime;
static BOOL bGetLastAlarms = FALSE;		/* Get last four alarms */
static BOOL bFileError = FALSE;
static unsigned char yDelay;			/* Read wait time */
static int yLockWait = 0;			/* Seconds to wait to lock serial port */
static char devLCKfile[255] = ttyLCKloc;
static char devLCKfileNew[255];
static struct termios oldtio;			/* current serial device configuration */

/* local functions */
static int GetParms(int argc, char *argv[]);
static void Version();

/*--------------------------------------------------------------------------
    main
----------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
    int fdser;				/* serial device fd */
    FILE *fdserlck;
    struct termios newtio;		/* serial device configuration */
    int rc = 0;
    time_t timeValue;
    struct tm timStruct;
    long rPID;
    int bRead, bWrite, lckCNT;
    char cmdFile[255];
    char command[255];
    char *SubStrPos = NULL;

    lastcommtv.tv_sec = 0;
    lastcommtv.tv_usec = 0;
    timeValue = time(NULL);
    timStruct = *(localtime(&timeValue));
    strftime(RunTime,18,"%Y%m%d-%H:%M:%S ",&timStruct);

    PID = getpid();

    /* Get command line parms */
    if ((!GetParms(argc, argv) && !bVersion) || bHelp) {
        printf("\nSee http://www.curtronics.com/Solar/ for Solar Site example\n\n");
        printf("Usage: %s [Options] Device\t\t\tv%s\n\n",ProgramName,VersionM);
        printf("Options:\n");
        printf(" -A, --last-alarms              Get last four alarms (once displayed FIFO queue is cleared)\n");
        printf(" -b, --verbose                  Verbose mode\n");
        printf(" -C, --calc-value=num           Calculate $ value using num * KWh\n");
        printf(" -c, --columnize                Output data in columns --> for -d, -e, -D, & -E options only\n");
        printf("                                  will disable all other options -- if value ends with an \"*\"\n");
        printf("                                  reporting of that item may not be in inverters firmware\n");
        printf(" -d, --get-dsp                  Get DSP data\n");
        printf(" -D, --get-dsp-extended         Get more DSP data\n");
        printf(" -e, --get-energy               Get Cumulated Energy readings\n");
        printf(" -E, --get-dsp-3phase           Get 3-Phase DSP data\n");
        printf(" -f, --firmware-ver             Query for Firmware Version string\n");
        printf(" -g, --mfg-date                 Query for Inverter Manufacturing Date\n");
        printf(" -h, --help                     This text\n");
        printf(" -i, --get-count                Display Inverter Time Counters\n");
        printf(" -j, --get-joules               Display Energy cumulated in the last 10 seconds\n");
        printf(" -l, --delay=num                Cmd Delay in 1/10ths seconds. Default is 1 (0.1 sec)\n");
        printf(" -m, --get-conf                 Query for Inverter System Configuration\n");
        printf(" -M, --use-mysql                 Put data into mysql database instead\n");
        printf(" -n, --serial-number            Query for Inverter Serial Number\n");
        printf(" -o, --output-file=<filename>   Append data to file (Created if nonexistant)\n");
        printf(" -p, --part-number              Query for Inverter Part Number\n");
        printf(" -P, --comm-pause=num           Wait <num> uS between sending commands to inverter (1-1000000)\n");
	printf(" -R, --read-timeout=num		Timeout value when reading data from the Inverter (mS)\n");
        printf(" -r, --calc-grid-power          Calc Grid power using Grid Voltage * Grid Current,\n");
        printf("                                  instead of reporting the Inverter's value. --> for \n");
        printf("                                  -d option only, ignored when used with -c option\n");
        printf("                                  (Inverter typically reports a lower value. This\n");
        printf("                                  affects Inverter conversion efficiency value.)\n");
        printf(" -S, --set-time                 Set Inverter Date/Time to system time\n");
        printf(" -s, --get-state                Get Inverter State\n");
        printf(" -T, --get-loctime              Display computer Date/Time\n");
        printf(" -t, --get-invtime              Display Inverter Date/Time\n");
        printf(" -V, --version                  Aurora communications program version\n");
        printf(" -v, --inv-version              Query for Version string\n");
	printf(" -U, --read-pause=num           Pause 'num' milli-seconds after sending command to inverter before\n");
        printf("                                  reading response from inverter (1-10000)\n");
        printf(" -u, --rpt-read-pause           Report when/that pausing before read\n");
        printf(" -w, --lock-wait                Seconds to wait to lock serial port. (1-30)\n");
	printf(" -x, --xon-xoff			Enable XON/XOFF on the serial port.\n");
        printf(" -Y, --retries=num		Retry failed communications with inverter up to 'num' times (1-100)\n");
        printf(" -y, --rpt-retries		Report the number of retires done\n");
        printf("\n -a, --address=num              Inverter address. (1-31) *** Required parameter ***\n");
        printf(" Device                         Serial Device.           *** Required parameter ***\n");
        printf("\n");
        if (bHelp) 
            exit(0);
        else
            exit(2);
    }

    if(bVerbose) fprintf(stderr, "\nRunTime %s\n",RunTime);

    if(bVerbose) fprintf (stderr, "PID : %ld\n", PID);

    if (bVersion) {
        Version();
        exit(0);
    }

    if(bVerbose) fprintf(stderr, "\nAttempting to get lock on Serial Port %s...  ",szttyDevice);
    fdserlck = fopen(devLCKfile, "a");
    if (fdserlck == NULL) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for write.\n\n",RunTime,ProgramName,devLCKfile);
        exit(2);
    }
    bWrite = fprintf(fdserlck, "%ld\n", PID);
    fclose(fdserlck);

    rPID = 0;
    lckCNT = -1;
    while(rPID != PID && lckCNT++ < yLockWait) {
        fdserlck = fopen(devLCKfile, "r");
        if (fdserlck == NULL) {
            if(bVerbose) fprintf(stderr, "\n");
            fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for read.\n\n",RunTime,ProgramName,devLCKfile);
            exit(2);
        }
        bRead = fscanf(fdserlck, "%ld", &rPID);
        fclose(fdserlck);
        sprintf(cmdFile,"/proc/%ld/cmdline",rPID);
        fdserlck = fopen(cmdFile, "r");
        if (fdserlck != NULL) {
            bRead = fscanf(fdserlck, "%s", command);
            fclose(fdserlck);
            SubStrPos = strstr(command, ProgramName);
        } else
            SubStrPos = NULL;
        if(bVerbose) fprintf (stderr, "\nrPID: %ld SubStrPos: %s command: %s",rPID,SubStrPos,command);
        if (rPID != PID) {
             if (SubStrPos == NULL) {
                 if(bVerbose) fprintf (stderr, "\n");
                 fprintf(stderr, "%s: %s: Clearing stale serial port lock. (%ld)\n",RunTime,ProgramName,rPID);
                 ClrSerLock(rPID);
             } else if (yLockWait > 0)
                 sleep(1);
        }
    }
    if (bVerbose && rPID == PID) fprintf(stderr, " Appears we got the lock.\n");
    if (rPID != PID) {
        ClrSerLock(PID);
        if(bVerbose) fprintf (stderr, "\n");
        fprintf(stderr, "%s: %s: Problem locking serial device %s, couldn't get the lock for %ld, locked by %ld.\n\n",RunTime,ProgramName,szttyDevice,PID,rPID);
        exit(2);
    }

    if(bVerbose) fprintf(stderr, "\nOpening Serial Port %s...  ", szttyDevice);
    fdser = open(szttyDevice, O_RDWR | O_NOCTTY );
    if (fdser < 0) {
        ClrSerLock(PID);
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "%s: %s: Problem opening serial device, check device name.\n\n",RunTime,ProgramName);
        exit(2);
    }
    if(bVerbose) fprintf(stderr, "Serial Port %s successfully opened.\n", szttyDevice);

    tcgetattr(fdser, &oldtio);      /* save previous port settings */

    memset(&newtio, 0, sizeof(newtio));

    newtio.c_cflag &= ~CRTSCTS;			/* disable hardware flow control */
    newtio.c_cflag &= ~PARENB;			/* no parity */
    newtio.c_cflag &= ~CSTOPB;			/* on stop bit */
    newtio.c_cflag &= ~CSIZE;			/* character size mask */
    newtio.c_cflag &= ~HUPCL;			/* no hangup */
    newtio.c_cflag |= CS8 | CLOCAL | CREAD;	/* 8 bit - ignore modem control lines - enable receiver */
    if (bXonXoff)
        newtio.c_iflag |= IXON | IXOFF;		/* enable XON/XOFF flow control on output & input */
    else {
        newtio.c_iflag &= ~IXON;		/* disable XON/XOFF flow control on output */
        newtio.c_iflag &= ~IXOFF;		/* disable XON/XOFF flow control on input */
    }
    newtio.c_iflag |= IGNBRK | IGNPAR;		/* ignore BREAK condition on input & framing errors & parity errors */
    newtio.c_oflag = 0;	    			/* set serial device input mode (non-canonical, no echo,...) */
    newtio.c_oflag &= ~OPOST;			/* enable output processing */
    newtio.c_lflag = 0;
    newtio.c_cc[VTIME]    = yDelay;		/* timeout in 1/10 sec intervals */
    newtio.c_cc[VMIN]     = 0;			/* block until char or timeout */

    if (cfsetospeed (&newtio, B19200)) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "\n%s: %s: Problem setting serial output speed.\n\n",RunTime,ProgramName);
        if(bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice);
        if(close(fdser)) {
            if(bVerbose) fprintf(stderr, "\n");
            fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName);
        }
        if (bVerbose) { fprintf(stderr, " Success!\n"); }
        ClrSerLock(PID);
        exit(2);
    }
    if (cfsetispeed (&newtio, B19200)) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "\n%s: %s: Problem setting serial input speed.\n\n",RunTime,ProgramName);
        if(bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice);
        if(close(fdser)) {
            if(bVerbose) fprintf(stderr, "\n");
            fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName);
        }
        if (bVerbose) { fprintf(stderr, " Success!\n"); }
        ClrSerLock(PID);
        exit(2);
    }

    if (bVerbose) { fprintf(stderr, "Configuring serial device... Flushing unread data first... "); }
    errno = 0;
    if(tcflush(fdser, TCIFLUSH)) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",RunTime,ProgramName,errno,strerror (errno));
    }
    if(tcsetattr(fdser, TCSANOW, &newtio)) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "\n%s: %s: Problem configuring serial device.\n\n",RunTime,ProgramName);
        if(bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice);
        if(close(fdser)) {
            if(bVerbose) fprintf(stderr, "\n");
            fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName);
        }
        if (bVerbose) { fprintf(stderr, " Success!\n"); }
        ClrSerLock(PID);
        exit(2);
    }

    if (bVerbose) { fprintf(stderr, " Success!\nFlushing serial device buffer..."); }

    errno = 0;
    if(tcflush(fdser, TCIOFLUSH)) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",RunTime,ProgramName,errno,strerror (errno));
        if(bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice);
        if(close(fdser)) {
            if(bVerbose) fprintf(stderr, "\n");
            fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName);
        }
        if (bVerbose) { fprintf(stderr, " Success!\n"); }
        ClrSerLock(PID);
        exit(2);
    }

    if (bVerbose) { fprintf(stderr, " Success!\n"); }

    bCommCheck = TRUE;
    if (bVerbose) { fprintf( stderr, "\nComm Check: Let's see if the Aurora is listening... "); }
    outfp = stderr;
    if (CommCheck(fdser,yAddress) >= 0) {
        if(bVerbose) fprintf(stderr, "Comm Check: OK\n");
    } else {
        if(bVerbose)
             fprintf(stderr, "Comm Check: Failure, aborting...\n");
        else {
             if (bRptReadPause) fprintf(stderr, "\n");
             fprintf(stderr, "%s: %s: No response after %i attempts\n",RunTime,ProgramName,yMaxAttempts);
        }
        rc = -1;
    }
    bCommCheck = FALSE;

    if (rc == 0 && bSetTime) {
        if(!bGetInvTime) bGetLocTime = TRUE;
        rc = SetTime(fdser,yAddress);
    }

    if (*outfile != '\0') {
        if (! (outfp = fopen(outfile, "a"))) {
            fprintf(stderr, "%s: %s: Problem opening output file %s\n",RunTime,ProgramName,outfile);
            bFileError = TRUE;
        }
    } else {
        outfp = stdout;
    }

    if (! bFileError) {

        bGetTime = bGetInvTime | bGetLocTime;

        if (rc == 0 && bGetTime)
            rc |= GetTime(fdser,yAddress);

        if (!bColumns) {

            if (rc == 0 && bGetPN)
                rc |= GetPN(fdser,yAddress);

            if (rc == 0 && bGetSN)
                rc |= GetSN(fdser,yAddress);

            if (rc == 0 && bGetVerFW)
                rc |= GetVerFW(fdser,yAddress);

            if (rc == 0 && bGetMfg)
                rc |= GetMfgDate(fdser,yAddress);

            if (rc == 0 && bGetVer)
                rc |= GetVer(fdser,yAddress);

            if (rc == 0 && bGetConf)
                rc |= GetConf(fdser,yAddress);

            if (rc == 0 && bGetJoules)
                rc |= GetJoules(fdser,yAddress);

            if (rc == 0 && bGetState)
                rc |= GetState(fdser,yAddress);

            if (rc == 0 && bGetCount)
                rc |= GetCounters(fdser,yAddress);

            if (rc == 0 && bGetLastAlarms)
                rc |= GetLastAlarms(fdser,yAddress);
                
            if (rc == 0 && bGetMDE)
                rc |= GetMDE(fdser,yAddress);
                if(bVerbose) fprintf(stderr, "\nDone GetMDE.\n\n");
        }

        if (rc == 0 && bGetDSP)
            rc |= GetDSP(fdser,yAddress);

        if (rc == 0 && bGetEnergy)
            rc |= GetCE(fdser,yAddress);

        if (rc == 0 && bGetDSPExtended)
            rc |= GetDSPExtended(fdser,yAddress);


        if (rc == 0 && bGetDSP3Phase)
            rc |= GetDSP3Phase(fdser,yAddress);

        if(bVerbose) fprintf(stderr, "\nrc: %d\n",rc);
    }
    if(rc >= 0) {
        if (bColumns && bColOutput)
            fprintf(outfp, "  OK\n");
        else
            fprintf(outfp, "\n");
        if(bVerbose) fprintf(stderr, "\nComplete.\n\n");
    } else if (bColumns && bColOutput)
        fprintf(outfp, "\n");

    if (*outfile != '\0' && ! bFileError) fclose(outfp);


    /* all done, exit */
    if(bVerbose) fprintf(stderr, "\nReady to close port.\n\n");
    RestorePort(fdser);
    if(bVerbose) fprintf(stderr, "\nRestored Port.\n\n");
    ClrSerLock(PID);

    if(bVerbose) fprintf(stderr, "\nComplete.\n\n");

    if(rc >= 0) exit(0);
    exit(1);
}

/*--------------------------------------------------------------------------
    RestorePort
    Restore Serial Port Settings
----------------------------------------------------------------------------*/
int RestorePort(int fdser) {

    if(bVerbose) fprintf(stderr, "Restoring Serial Port settings %s...", szttyDevice);
    if(tcsetattr(fdser, TCSANOW, &oldtio)) {		/* restore previous port settings */
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "%s: %s: Problem restoring serial device settings.\n",RunTime,ProgramName);
        if(bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice);
        if(close(fdser)) {
            if(bVerbose) fprintf(stderr, "\n");
            fprintf(stderr, "%s: %s: Problem closing serial device, check device name.\n",RunTime,ProgramName);
        }
        if(bVerbose) fprintf(stderr, " Success!\n");
        return 2;
    }

    if (bVerbose) { fprintf(stderr, " Success!\nFlushing serial device buffer..."); }

    errno = 0;
    if(tcflush(fdser, TCIOFLUSH)) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",RunTime,ProgramName,errno,strerror (errno));
        if(bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice);
        if(close(fdser)) {
            if(bVerbose) fprintf(stderr, "\n");
            fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName);
        }
        if (bVerbose) { fprintf(stderr, " Success!\n"); }
        return 2;
    }

    if(bVerbose) fprintf(stderr, " Success!\nClosing Serial Port %s...", szttyDevice);

    if(close(fdser)) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName);
        return 2;
    }
    if(bVerbose) fprintf(stderr, " Success!\n");
    return 0;
}


/*--------------------------------------------------------------------------
    ClrSerLock
    Clear Serial Port lock.
----------------------------------------------------------------------------*/
int ClrSerLock(long PID) {
    FILE *fdserlck, *fdserlcknew;
    long rPID;
    int bWrite, bRead;

    if(bVerbose) fprintf(stderr, "\ndevLCKfile <%s>\ndevLCKfileNew <%s>\nClearing Serial Port Lock (%ld)...", devLCKfile, devLCKfileNew, PID);
    fdserlck = fopen(devLCKfile, "r");
    if (fdserlck == NULL) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "%s: %s: Problem opening serial device lock file to clear PID %ld: %s for read.\n\n",RunTime,ProgramName,PID,devLCKfile);
        return(0);
    }
    fdserlcknew = fopen(devLCKfileNew, "w");
    if (fdserlcknew == NULL) {
        if(bVerbose) fprintf(stderr, "\n");
        fprintf(stderr, "%s: %s: Problem opening new serial device lock file to clear PID %ld: %s for write.\n\n",RunTime,ProgramName,PID,devLCKfileNew);
        fclose(fdserlck);
        return(0);
    }
    bRead = fscanf(fdserlck, "%ld", &rPID);
    while (bRead != EOF) {
        if (rPID != PID) bWrite = fprintf(fdserlcknew, "%ld\n", rPID);
        bRead = fscanf(fdserlck, "%ld", &rPID);
    }
    fclose(fdserlck);
    fclose(fdserlcknew);
    rename(devLCKfileNew,devLCKfile);
    if(bVerbose) fprintf(stderr, " done.\n");

    return -1;
}

/*--------------------------------------------------------------------------
    GetParms
    Reads command line parameters.
----------------------------------------------------------------------------*/
int GetParms(int argc, char *argv[])
{
    extern char *optarg;
    extern int optind, opterr, optopt;
    int c;
    int i = 0;
    BOOL b = FALSE;
    char *pos;

    if (strchr(VersionM,'x') != NULL) fprintf(stderr, "\n**** THIS IS EXPERIMENTAL CODE ****\n"); 

    while (! b && i < argc) if (strcmp(argv[i++],"-b") == 0) b = TRUE;

    if(b) {
        fprintf(stderr, "\n");
        i = 0;
        while (i < argc) fprintf(stderr, "%s ",argv[i++]);
        fprintf(stderr, "\n");
   }

     /* options descriptor */
    static struct option longopts[] = {
        { "address",		required_argument,	0,	'a' },
	{ "last-alarms",	no_argument,		0,	'A' },
        { "verbose",		no_argument,		0,	'b' },
	{ "calc-value",		required_argument,	0,	'C' },
        { "columnize",		no_argument,		0,	'c' },
        { "get-dsp",		no_argument,		0,	'd' },
        { "get-dsp-extended",	no_argument,		0,	'D' },
        { "get-dsp-3phase",     no_argument,		0,	'E' },
        { "get-energy",		no_argument,		0,	'e' },
        { "firmware-ver",	no_argument,		0,	'f' },
        { "mfg-date",		no_argument,		0,	'g' },
        { "hide-dsp-msg",	no_argument,		0,	'H' },
        { "help",		no_argument,		0,	'h' },
        { "get-count",		no_argument,		0,	'i' },
        { "get-joules",		no_argument,		0,	'j' },
	{ "delay",		required_argument,	0,	'l' },
	{ "output-file",	required_argument,	0,	'o' },
        { "get-conf",		no_argument,		0,      'm' },
        { "use-mysql",  no_argument,    0,      'M'},
        { "serial-number",	no_argument,		0,	'n' },
        { "part-number",	no_argument,		0,	'p' },
        { "comm-pause",		required_argument,	0,	'P' },
        { "read-timeout",	required_argument,	0,	'R' },
        { "calc-grid-power",	no_argument,		0,	'r' },
        { "set-time",		no_argument,		0,	'S' },
        { "get-state",		no_argument,		0,	's' },
        { "get-invtime",	no_argument,		0,      't' },
        { "get-loctime",	no_argument,		0,	'T' },
        { "version",		no_argument,		0,	'V' },
        { "inv-version",	no_argument,		0,	'v' },
	{ "read-pause",		required_argument,	0,	'U' },
	{ "rpt-read-pause",	no_argument,		0,	'u' },
        { "lock-wait",		required_argument,	0,	'w' },
	{ "xon-xoff",		no_argument,		0,	'x' },
	{ "retries",		required_argument,	0,	'Y' },
	{ "rpt-retries",	no_argument,		0,	'y' },
        { NULL,				0,				NULL,   0 }
    };

    /* Set command line defaults */
    *szttyDevice = '\0';
    *outfile = '\0';
    yDelay = 1;

    if(argc == 1)
        return 0;				/* no parms at all */

    while ((c = getopt_long(argc, argv, "a:AbC:cDdEefgHhijl:m:Mno:P:pR:rSsTtVvU:uw:xY:y", longopts, NULL )) != EOF) {
        switch (c) {
            case 'a':
                /* Inverter address */
                yAddress = atoi(optarg);
                break;
            case 'A': bGetLastAlarms     = TRUE; break;
            case 'b': bVerbose     = TRUE; break;
            case 'C':
                yCost = atof(optarg);
                break;
            case 'c': bColumns     = TRUE; break;
            case 'd': bGetDSP      = TRUE; break;
            case 'D': bGetDSPExtended    = TRUE; break;
            case 'E': bGetDSP3Phase      = TRUE; break;
            case 'e': bGetEnergy   = TRUE; break;
            case 'f': bGetVerFW    = TRUE; break;
            case 'g': bGetMfg      = TRUE; break;
            case 'H': bHideDSP     = TRUE; break;
            case 'h': bHelp        = TRUE; break;
            case 'i': bGetCount    = TRUE; break;
            case 'j': bGetJoules   = TRUE; break;
            case 'l':
                /* Get delay time */
                i = atoi(optarg);
                if(i < 0 || i > 255) {
                        fprintf(stderr, "\n%s: %s: Illegal delay specified.\n",RunTime,ProgramName);
                        return 0;
                }
                yDelay = (unsigned char)i;
                break;
            case 'm': bGetConf     = TRUE; break;
            case 'M': bGetMDE     = TRUE; break;
            case 'n': bGetSN       = TRUE; break;
            case 'o': strcpy(outfile, optarg); break;
            case 'p': bGetPN       = TRUE; break;
            case 'P':
                yCommPause = atoi(optarg);
                if (yCommPause <= 0 || yCommPause > 1000000) {
                    fprintf(stderr, "\n%s: %s: Comm Pause micro-seconds (%li) out of range, 1-1000000.\n",RunTime,ProgramName,yCommPause);
                    return 0;
                }
                break;
            case 'R':
                /* read timeout value in uS */
                yTimeout = atoi(optarg)*1000;
                break;
            case 'r': bCalcGridPwr = TRUE; break;
            case 'S': bSetTime     = TRUE; break;
            case 's': bGetState    = TRUE; break;
            case 'T': bGetLocTime  = TRUE; break;
            case 't': bGetInvTime  = TRUE; break;
            case 'U':
                yReadPause = atoi(optarg);
                if (yReadPause < 1 || yReadPause > 10000) {
                    fprintf(stderr, "\n%s: %s: Read Pause milli-seconds (%d) out of range, 1-10000.\n",RunTime,ProgramName,yReadPause);
                    return 0;
                }
                break;
            case 'u': bRptReadPause	= TRUE; break;
            case 'w':
                yLockWait = atoi(optarg);
                if (yLockWait < 1 || yLockWait > 30) {
                    fprintf(stderr, "\n%s: %s: Lock Wait seconds (%d) out of range, 1-30.\n",RunTime,ProgramName,yLockWait);
                    return 0;
                }
                break;
            case 'x': bXonXoff     = TRUE; break;
            case 'Y':
                yMaxAttempts = atoi(optarg);
                if (yMaxAttempts < 1 || yMaxAttempts > 100) {
                    fprintf(stderr, "\n%s: %s: Retries (%d) out of range, 1-100.\n",RunTime,ProgramName,yMaxAttempts);
                    return 0;
                }
                break;
            case 'y': bRptReties   = TRUE; break;
            case 'V': bVersion     = TRUE; break;
            case 'v': bGetVer      = TRUE; break;

            case '?': /* user entered unknown option */
            case ':': /* user entered option without required value */
                return 0;

            default:
                break;
        }
    }
    if (optind < argc) 			/* get serial device name */
        strcpy(szttyDevice, argv[optind]);
     else {
        if(!bVersion && ! bHelp) fprintf(stderr, "\n%s: %s: No serial device specified\n",RunTime,ProgramName);
        return 0;
    }
    pos = strrchr(szttyDevice, '/');
    if (pos > 0) {
        pos++;
        strcat(devLCKfile, pos);
        sprintf(devLCKfileNew,"%s.%ld",devLCKfile,PID);
    } else {
        *devLCKfile = '\0';
    }
    if(bVerbose) fprintf(stderr, "\nszttyDevice: %s\nyDelay:  %i\nyTimeout %i uS\ndevLCKfile: <%s>\ndevLCKfileNew: <%s>\n",szttyDevice,yDelay,yTimeout,devLCKfile,devLCKfileNew);

    if (yAddress < 1 || yAddress > 31) {
        fprintf(stderr, "\n%s: %s: Illegal address (%d) specified.\n",RunTime,ProgramName,yAddress);
        return 0;
    }
    return -1;
}

/*--------------------------------------------------------------------------
    Version
    Display program component versions
----------------------------------------------------------------------------*/
void Version()
{
    printf("\nAurora module versions:\n");
    printf("Main module : %6s\n",VersionM);
    printf("Comm module : %6s\n",VersionC);
    printf("main.h      : %6s\n",VersionMHc);
    printf("comm.h      : %6s\n",VersionCHc);
    printf("names.h     : %6s\n",VersionNHc);
    printf("states.h    : %6s\n\n",VersionSHc);
}
 
